package com.jdy.utils;


import java.util.*;

/**
 * Spring 框架学习
 * <p>
 * [Description]
 * <p>
 * 创建人 Dale 时间 2020/3/9 20:33
 */
public class StringUtils {
    /**
     * 空的字符串数组
     */
    static final String[] EMPTY_STRING_ARRAY = {};

    /**
     * 空的字符串
     */
    static final String EMPTY_STRING = "";

    /**
     * 值为空的字符串对象
     */
    static final String NULL_STRING = "null";

    /**
     * window系统下的路径分隔符
     */
    private static final String WINDOWS_FOLDER_SEPARATOR = "\\";

    /**
     * 路径分隔符
     */
    private static final String FOLDER_SEPARATOR = "/";

    /**
     * 顶层路径
     */
    private static final String TOP_PATH = "..";

    /**
     * 当前路径
     */
    private static final String CURRENT_PATH = ".";


    /**
     * 检查字符串是否为空
     * 但字符串为 “ ”返回也是true，如果检查的字符串中 “ ”字符也要检测请调用{@link #isBlack(CharSequence)}方法
     *
     * @param str 被检查的字符串
     * @return true ： 字符串为空； false： 字符串不为空
     */
    public static boolean isEmpty(CharSequence str) {
        return null == str || str.toString().isEmpty();
    }

    public static boolean isNotEmpty(CharSequence str) {
        return !isEmpty(str);
    }

    /**
     * 检查字符串是否为空。
     * <p>
     * 此方法基于{@link #isEmpty(CharSequence)}方法，与之相别的是在其基础上添加了两个判定条件：
     * 1.字符串不能全是空白字符
     * 2.如果字符串为"null"也将会被认为是{@code true}
     * <p>
     * 例如： “  ” 与 “null”将会被判定为{@code true}
     *
     * @param str 被检查的字符串
     * @return true ： 字符串为空； false： 字符串不为空
     * @see #isEmpty(CharSequence)
     * @see #hasText(CharSequence)
     */
    public static boolean isBlack(CharSequence str) {
        return !isNotBlack(str);
    }

    public static boolean isNotBlack(CharSequence str) {
        return isNotEmpty(str) && hasText(str);
    }

    /**
     * 将字符串{@code target}首字母小写
     * <p>
     * 例如： “ABCD”字符串将会变成 “aBCD”;
     * 需要注意的是“aBCD”将不会变动
     *
     * @param target 需转换的字符串
     * @return 首字母小写的字符串
     */
    public static String lowerFirstCase(String target) {
        return changeFirstCase(target, false);
    }

    /**
     * 将{@code String}字符串的首字母大写
     * <p>
     * 根据{@link Character#toUpperCase(char)}将第一个字母改为大写, 其他字母不改变
     *
     * @param text 要大写的{@code String}
     * @return 大写的{@code String}
     */
    public static String upperFirstCase(String text) {
        return changeFirstCase(text, true);
    }

    /**
     * 将给定{@code String}字符串的首字母,改成大写或者小写
     *
     * @param text       需要变更的字符串
     * @param capitalize 是否大写首字母
     * @return 变更后的字符串
     */
    private static String changeFirstCase(String text, boolean capitalize) {
        if (isBlack(text)) return text;

        char[] chars = text.toCharArray();
        char baseChar = chars[0];
        char updatedChar;

        if (capitalize) {//将首字母大写
            updatedChar = Character.toUpperCase(baseChar);
        } else {//将首字母小写
            updatedChar = Character.toLowerCase(baseChar);
        }

        if (baseChar == updatedChar) {
            return text;
        }
        chars[0] = updatedChar;
        return new String(chars, 0, chars.length);
    }

    /**
     * 将String的集合转换成String数组
     * <p>
     * 此方法基于{@link Collection#toArray(Object[])}方法实现
     * 如果给定的{@code Collection}对象是空值，则返回一个空的字符串数组
     *
     * @param collection String集合对象
     * @return String数组
     */
    public static String[] toStringArray(Collection<String> collection) {
        return CollectionUtils.isEmpty(collection) ? EMPTY_STRING_ARRAY : collection.toArray(EMPTY_STRING_ARRAY);
    }

    public static String[] toStringArray(Enumeration<String> collection) {
        return collection == null ? EMPTY_STRING_ARRAY : toStringArray(Collections.list(collection));
    }

    public static String[] tokenizeToStringArray(String text, String delimiters) {
        return tokenizeToStringArray(text, delimiters, true, true);
    }

    public static String[] tokenizeToStringArray(String text, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
        if (text == null) {
            return EMPTY_STRING_ARRAY;
        }

        StringTokenizer st = new StringTokenizer(text, delimiters);
        List<String> tokens = new ArrayList<>();
        while (st.hasMoreTokens()) {
            String token = st.nextToken();
            if (trimTokens) {
                token = token.trim();
            }

            if (!ignoreEmptyTokens || token.length() > 0) {
                tokens.add(token);
            }
        }
        return toStringArray(tokens);
    }

    /**
     * 规范化路径
     * <p>
     * 通过抑制“path/”和内部简单点等序列来规范化路径。
     * <p>
     * 结果便于路径比较。对于其他用途，请注意Windows分隔符（“\”）被简单的斜杠替换。
     *
     * @param path 原路径
     * @return 规范化路径
     */
    public static String cleanPath(String path) {
        if (isEmpty(path)) return path;

        String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR);

        //没有后缀了
        if (pathToUse.indexOf('.') == -1) return pathToUse;

        int prefixIndex = pathToUse.indexOf(':');
        String prefix = "";
        if (prefixIndex != -1) {
            prefix = pathToUse.substring(0, prefixIndex + 1);
            if (prefix.contains(FOLDER_SEPARATOR)) {
                prefix = "";
            } else {
                pathToUse = pathToUse.substring(prefixIndex + 1);
            }
        }

        if (pathToUse.startsWith(FOLDER_SEPARATOR)) {
            prefix = prefix + FOLDER_SEPARATOR;
            pathToUse = pathToUse.substring(1);
        }

        String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR);
        LinkedList<String> pathElements = new LinkedList<>();
        int tops = 0;
        for (int i = pathArray.length - 1; i >= 0; i--) {
            String element = pathArray[i];
            if (CURRENT_PATH.equals(element)) {
                // Points to current directory - drop it.
            } else if (TOP_PATH.equals(element)) {
                // Registering top path found.
                tops++;
            } else {
                if (tops > 0) {
                    // Merging path element with element corresponding to top path.
                    tops--;
                } else {
                    // Normal path element found.
                    pathElements.add(0, element);
                }
            }
        }

        // Remaining top paths need to be retained.
        for (int i = 0; i < tops; i++) {
            pathElements.add(0, TOP_PATH);
        }

        if (pathElements.size() == 1 && "".equals(pathElements.getLast()) && !prefix.endsWith(FOLDER_SEPARATOR)) {
            pathElements.add(0, CURRENT_PATH);
        }

        return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);
    }

    public static String collectionToDelimitedString(Collection<?> coll, String delim) {
        return collectionToDelimitedString(coll, delim, EMPTY_STRING, EMPTY_STRING);
    }

    public static String collectionToDelimitedString(Collection<?> coll, String delim, String prefix, String suffix) {
        if (CollectionUtils.isEmpty(coll)) {
            return EMPTY_STRING;
        }

        StringBuilder sb = new StringBuilder();
        Iterator<?> it = coll.iterator();
        while (it.hasNext()) {
            sb.append(prefix).append(it.next()).append(suffix);
            if (it.hasNext()) {
                sb.append(delim);
            }
        }
        return sb.toString();
    }


    public static String[] delimitedListToStringArray(String str, String delimiter) {
        return delimitedListToStringArray(str, delimiter, null);
    }

    public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) {
        if (null == str) return EMPTY_STRING_ARRAY;

        if (delimiter == null) {
            return new String[]{str};
        }

        List<String> result = new ArrayList<>();
        if (delimiter.isEmpty()) {
            for (int i = 0; i < str.length(); i++) {
                result.add(deleteAny(str.substring(i, i + 1), charsToDelete));
            }
        } else {
            int pos = 0;
            int delPos;
            while ((delPos = str.indexOf(delimiter, pos)) != -1) {
                result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
                pos = delPos + delimiter.length();
            }

            if (str.length() > 0 && pos <= str.length()) {
                // Add rest of String, but not in case of empty input.
                result.add(deleteAny(str.substring(pos), charsToDelete));
            }
        }

        return toStringArray(result);
    }

    public static String deleteAny(String inString, String charsToDelete) {
        if (isEmpty(inString) || isEmpty(charsToDelete)) return inString;

        StringBuilder sb = new StringBuilder(inString.length());
        for (int i = 0; i < inString.length(); i++) {
            char c = inString.charAt(i);
            if (charsToDelete.indexOf(c) == -1) {
                sb.append(c);
            }
        }

        return sb.toString();
    }

    /**
     * 键查给定的字符串是否包含文本内容
     *
     * @param str 给定字符串
     * @return true ： {code str}不能为空，且至少包含一个非空白字符
     */
    public static boolean hasText(CharSequence str) {
        if (isEmpty(str)) return false;
        return containsText(str);
    }

    /**
     * 检查给定的{@code String}是否包含文本内容
     * <p>
     * 其实这个方法相当于调用{@link String#trim()}后再调用{@link String#isEmpty()}方法，
     * 要求{@code String str} 不能全部是空格字符串，即至少包含一个非空白字符
     *
     * @param str 给定字符串
     * @return {@code true}： 包含至少一个非空白字符的字符串
     */
    private static boolean containsText(CharSequence str) {
        for (int i = 0; i < str.length(); i++) {
            //如果出现非空白字符，则认为str包含文本内容
            if (!Character.isWhitespace(str.charAt(i))) {
                return true;
            }
        }
        return false;
    }

    /**
     * 替换，将将文本信息中的指定内容替换成新的类容，
     * 此方法与{@link String#replaceAll(String, String)}方法相似,但也有不同
     * {@link String#replaceAll(String, String)}的第一个参数是一个正则（如果是字符串，
     * 则先会调用{@link java.util.regex.Pattern#compile(String)}方法将其转换成正则），然后进行匹配转换
     * 因此会出现转换失败的情况，
     * <p>
     * 此方法基于{@link String#indexOf(String)}方法，直接进行检测，如果检测存在，则进行替换，然后继续检测下一个
     *
     * @param text       文本字符串
     * @param oldPattern 需检测的参数
     * @param newPattern 替换参数
     * @return 替换后的文本字符串
     */
    public static String replace(String text, String oldPattern, String newPattern) {
        if (isEmpty(text) || isEmpty(oldPattern) || newPattern == null) {
            return text;
        }

        int index = text.indexOf(oldPattern);
        if (index == -1) {
            //没有出现需要替代的元素，直接返回
            return text;
        }

        int capacity = text.length();
        if (newPattern.length() > oldPattern.length()) {
            capacity += 16;
        }

        StringBuilder sb = new StringBuilder(capacity);
        int pos = 0;  // 原来字符串位置
        int patLen = oldPattern.length();
        while (index >= 0) {
            sb.append(text, pos, index);
            sb.append(newPattern);
            pos = index + patLen;
            index = text.indexOf(oldPattern, pos);
        }

        // append any characters to the right of a match
        sb.append(text, pos, text.length());
        return sb.toString();
    }

    public static void main(String[] args) {
        String[] array = {"Jane", "Dale", "JDY", "AAA"};
        System.out.println(arrayToDelimitedString(array, "、"));
    }

    /**
     * 将数组连接成字符串
     *
     * @param array     数组
     * @param delimiter 分隔符
     * @return 连接后的字符串
     */
    public static String arrayToDelimitedString(Object[] array, String delimiter) {
        if (ObjectUtils.isEmpty(array)) {
            return EMPTY_STRING;
        }

        if (array.length == 1) {
            return nullSafeToString(array[0]);
        }

        StringJoiner joiner = new StringJoiner(delimiter);
        for (Object object : array) {
            joiner.add(String.valueOf(object));
        }

        return joiner.toString();
    }

    /**
     * 如果{@code object}不是{@code null}，则返回基于内容的字符串表示；否则返回空字符串。
     * <p>
     * 与{@link #nullSafeToString(Object)}不同的是，它返回一个空字符串，而不是{@code null}值的“null”。
     *
     * @param object 要为其生成显示字符串的对象
     * @return {@code object}的显示字符串表示形式
     * @see #nullSafeToString(Object)
     */
    public static String getDisplayString(Object object) {
        if (object == null) {
            return EMPTY_STRING;
        }
        return nullSafeToString(object);
    }

    public static String nullSafeToString(Object object) {
        if (object == null) {
            return NULL_STRING;
        }

        if (object instanceof String) {
            return object.toString();
        }

        if (object.getClass().isArray()) {
            Object[] array = (Object[]) object;
            return arrayToDelimitedString(array, ",");
        }

        String str = object.toString();
        return str == null ? EMPTY_STRING : str;
    }

    /**
     * 返回对象整体标识的字符串表示形式。
     *
     * @param object Object对象(可以是{@code null})
     * @return 以字符串表示形式返回对象的标识，如果对象是{@code null}，则返回空字符串
     */
    public static String identityToString(Object object) {
        if (null == object) return EMPTY_STRING;
        return object.getClass().getName() + '@' + getIdentityHexString(object);
    }

    /**
     * 对象的标识哈希代码的十六进制字符串形式
     * <p>
     * 此方法借助{@link System#identityHashCode(Object)}方法实现，获取对象在系统的唯一标识符
     *
     * @param object Object对象(不能为{@code null})
     * @return 对象的十六进制标识码
     */
    private static String getIdentityHexString(Object object) {
        return Integer.toHexString(System.identityHashCode(object));
    }

    public static String trimAllWhitespace(String str) {
        if (isEmpty(str)) return str;

        int len = str.length();
        StringBuilder sb = new StringBuilder(len);

        for (int i = 0; i < len; i++) {
            char c = str.charAt(i);
            if (Character.isWhitespace(c)) continue;
            sb.append(c);
        }

        return sb.toString();
    }
}
